Agenda

Announcements

  • Ch 17 programming notebook not assigned, but highly recommended
  • several steps take some non-trivial configuration that I don’t want to require per se

MDSR Ch 17: Towards big data

Biggest of the big… (for perspective)

What happens when data start getting big (in R)

Memory limits in R

Strategies when data get big

If things are just slow…

Recall the teller simulation:


any_active <- function(df) {
  # return TRUE if someone has not finished
  return(max(df$endtime) == Inf)
}

next_customer <- function(df) {
  # returns the next customer in line
  res <- filter(df, endtime == Inf) %>%
    arrange(arrival)
  return(head(res, 1))
}

update_customer <- function(df, cust_num, end_time) {
  # sets the end time of a specific customer
  return(mutate(df, endtime = ifelse(custnum == cust_num, end_time, endtime)))
}


teller_sim <- function(n = 1/2, m = 3/2, hours = 6) {
  # simulation of bank where there is just one teller
  # n: expected number of customers per minute
  # m: expected length of transaction is m minutes
  # hours: bank open for this many hours
  
  customers <- rpois(hours * 60, lambda = n)
  arrival <- numeric(sum(customers))
  position <- 1
  for (i in 1:length(customers)) {
    numcust <- customers[i]
    if (numcust != 0) {
      arrival[position:(position + numcust - 1)] <- rep(i, numcust)
      position <- position + numcust
    }
  }
  duration <- rexp(length(arrival), rate = 1/m)  # E[X]=m
  df <- data.frame(arrival, duration, custnum = 1:length(duration), 
                   endtime = Inf, stringsAsFactors = FALSE)
  
  endtime <- 0 # set up beginning of simulation
  while (any_active(df)) { # anyone left to serve
    next_one <- next_customer(df)
    now <- ifelse(next_one$arrival >= endtime, next_one$arrival, endtime)
    endtime <- now + next_one$duration
    df <- update_customer(df, next_one$custnum, endtime)
  }
  df <- mutate(df, totaltime = endtime - arrival)
  return(favstats(~ totaltime, data = df))
}

Profiling the teller simulation

Rprof("TellerSimProfile")
teller_sim()
Rprof(NULL)
head(summaryRprof("TellerSimProfile")$by.self, 20)

STILL slow? Try biglm

require(biglm)

n <- 20000
p <- 500
d <- as.data.frame(matrix(rnorm(n * (p + 1)), ncol = (p + 1)))
expl_vars <- paste(paste0("V", 2:(p+1)), collapse = " + ")
my_formula <- as.formula(paste("V1 ~ ", expl_vars))

# profile `lm` vs `biglm`
system.time(lm(my_formula, data = d))
   user  system elapsed 
  5.779   0.251   6.131 
system.time(biglm(my_formula, data = d))
   user  system elapsed 
  3.777   0.182   4.002 

Next step: parallel processing

my_cores <- detectCores()
my_cores
[1] 4
k <- 5

# without parallel processing
system.time(lapply(1:k, teller_sim))
   user  system elapsed 
 12.102   0.258  12.565 
# parallelize with 3 cores
system.time(mclapply(1:k, teller_sim, mc.cores = my_cores - 1))
   user  system elapsed 
 11.942   0.933  12.079 

MapReduce (parallelization that’s not embarassing?)

Hadoop & Spark

Interface with Spark

require(sparklyr)
# spark_install()   # only once per machine

Interface with Spark

# modify master to connect to a remote Spark cluster
sc <- spark_connect(master = "local")
* Using Spark: 2.4.0
class(sc)
[1] "spark_connection"       "spark_shell_connection" "DBIConnection"         
babynames_tbl <- 
  sc %>%
  copy_to(babynames::babynames, "babynames", overwrite = TRUE)

class(babynames_tbl)
[1] "tbl_spark" "tbl_sql"   "tbl_lazy"  "tbl"      

Counting Matthews

babynames_tbl %>%
  filter(name == "Matthew") %>%
  group_by(year) %>%
  summarise(N = n(), 
            total_births = sum(n)) %>%
  arrange(desc(total_births)) %>%
  head()
Missing values are always removed in SQL.
Use `SUM(x, na.rm = TRUE)` to silence this warning
This warning is displayed only once per session.

From dplyr to SQL

q <- 
  babynames_tbl %>%
  filter(name == "Matthew") %>%
  group_by(year) %>%
  summarise(N = n(), 
            total_births = sum(n)) %>%
  arrange(desc(total_births)) %>%
  head()

q
show_query(q)
<SQL>
SELECT `year`, count(*) AS `N`, SUM(`n`) AS `total_births`
FROM `babynames`
WHERE (`name` = "Matthew")
GROUP BY `year`
ORDER BY `total_births` DESC
LIMIT 6

Querying the Spark cluster

require(DBI)

dbGetQuery(conn = sc, statement = "
                         SELECT year, sum(1) as N, sum(n) as total_births
                         FROM babynames
                         WHERE name == 'Matthew'
                         GROUP BY year
                         ORDER BY total_births desc
                         LIMIT 6
                         ")

Modeling with Spark

require(macleish)

weather_tbl <- copy_to(sc, whately_2015, overwrite = TRUE)

weather_tbl %>%
  sparklyr::ml_linear_regression(rainfall ~ temperature + pressure + rel_humidity) %>%
  summary()
Deviance Residuals:
      Min        1Q    Median        3Q       Max 
-0.041290 -0.021761 -0.011632 -0.000576 15.968356 

Coefficients:
 (Intercept)  temperature     pressure rel_humidity 
   0.7177542    0.0004089   -0.0007545    0.0004377 

R-Squared: 0.004824
Root Mean Squared Error: 0.1982

Alternatives to SQL (Google BigQuery)

require(bigrquery)

project_id <- "stat-380-class-demo"   # Beckman's google cloud project ID

sql <- "SELECT word, count(distinct corpus) as numPlays, sum(word_count) as N
        FROM [publicdata:samples.shakespeare]
        GROUP BY word
        ORDER BY N desc
        LIMIT 10
        "
query_exec(query = sql, project = project_id)
Waiting for authentication in browser...
Press Esc/Ctrl + C to abort
Authentication complete.

Running job -:  1s:
Running job \:  2s:
Running job |:  2s:
Running job /:  3s:
Running job -:  3s:
Running job \:  3s:
Running job |:  4s:
Running job /:  4s:
Running job -:  5s:
Running job \:  5s:
Running job |:  5s:
Running job /:  5s:
                                                                               
10.0 megabytes processed

Rcpp

require(Rcpp)

# write a simple function in C++

cppFunction('int addemup(int x, int y, int z) {
  int sum = x + y + z;
  return sum;
}')


# R recognizes `addemup` like any other function
addemup
function (x, y, z) 
.Call(<pointer: 0x1176de8f0>, x, y, z)
addemup(2, 4, 6)
[1] 12

Stan

parameters {
  real y[2];
}
model {
  y[1] ~ normal(0, 1);
  y[2] ~ double_exponential(0, 2);
}
Loading required package: sp
library(rstan)
Loading required package: StanHeaders
rstan (Version 2.19.2, GitRev: 2e1f913d3ca3)
For execution on a local, multicore CPU with excess RAM we recommend calling
options(mc.cores = parallel::detectCores()).
To avoid recompilation of unchanged Stan programs, we recommend calling
rstan_options(auto_write = TRUE)

Attaching package: ‘rstan’

The following object is masked from ‘package:tidyr’:

    extract
fit <- sampling(ex1, cores = 3)
starting worker pid=25541 on localhost:11849 at 12:27:28.837
starting worker pid=25555 on localhost:11849 at 12:27:29.119
starting worker pid=25569 on localhost:11849 at 12:27:29.379

SAMPLING FOR MODEL 'stan-5a7c20ea0b45' NOW (CHAIN 1).
Chain 1: 
Chain 1: Gradient evaluation took 1.1e-05 seconds
Chain 1: 1000 transitions using 10 leapfrog steps per transition would take 0.11 seconds.
Chain 1: Adjust your expectations accordingly!
Chain 1: 
Chain 1: 
Chain 1: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 1: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 1: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 1: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 1: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 1: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 1: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 1: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 1: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 1: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 1: Iteration: 1800 / 2000 [ 90%]  (Sampling)

SAMPLING FOR MODEL 'stan-5a7c20ea0b45' NOW (CHAIN 2).
Chain 1: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 1: 
Chain 1:  Elapsed Time: 0.032539 seconds (Warm-up)
Chain 1:                0.031726 seconds (Sampling)
Chain 1:                0.064265 seconds (Total)
Chain 1: 
Chain 2: 
Chain 2: Gradient evaluation took 1.1e-05 seconds
Chain 2: 1000 transitions using 10 leapfrog steps per transition would take 0.11 seconds.
Chain 2: Adjust your expectations accordingly!
Chain 2: 
Chain 2: 
Chain 2: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 2: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 2: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 2: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 2: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 2: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 2: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 2: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 
SAMPLING FOR MODEL 'stan-5a7c20ea0b45' NOW (CHAIN 3).
2: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 3: 
Chain 3: Gradient evaluation took 9e-06 seconds
Chain 3: 1000 transitions using 10 leapfrog steps per transition would take 0.09 seconds.
Chain 3: Adjust your expectations accordingly!
Chain 3: 
Chain 3: 
Chain 3: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 2: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 2: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 2: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 2: 
Chain 2:  Elapsed Time: 0.033218 seconds (Warm-up)
Chain 2:                0.029568 seconds (Sampling)
Chain 2:                0.062786 seconds (Total)
Chain 2: 
Chain 3: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 3: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 3: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 3: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 3: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 3: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 3: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 3: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 3: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 3: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 3: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 3: 
Chain 3:  Elapsed Time: 0.033801 seconds (Warm-up)
Chain 3:                0.032489 seconds (Sampling)
Chain 3:                0.06629 seconds (Total)
Chain 3: 

SAMPLING FOR MODEL 'stan-5a7c20ea0b45' NOW (CHAIN 4).
Chain 4: 
Chain 4: Gradient evaluation took 6e-06 seconds
Chain 4: 1000 transitions using 10 leapfrog steps per transition would take 0.06 seconds.
Chain 4: Adjust your expectations accordingly!
Chain 4: 
Chain 4: 
Chain 4: Iteration:    1 / 2000 [  0%]  (Warmup)
Chain 4: Iteration:  200 / 2000 [ 10%]  (Warmup)
Chain 4: Iteration:  400 / 2000 [ 20%]  (Warmup)
Chain 4: Iteration:  600 / 2000 [ 30%]  (Warmup)
Chain 4: Iteration:  800 / 2000 [ 40%]  (Warmup)
Chain 4: Iteration: 1000 / 2000 [ 50%]  (Warmup)
Chain 4: Iteration: 1001 / 2000 [ 50%]  (Sampling)
Chain 4: Iteration: 1200 / 2000 [ 60%]  (Sampling)
Chain 4: Iteration: 1400 / 2000 [ 70%]  (Sampling)
Chain 4: Iteration: 1600 / 2000 [ 80%]  (Sampling)
Chain 4: Iteration: 1800 / 2000 [ 90%]  (Sampling)
Chain 4: Iteration: 2000 / 2000 [100%]  (Sampling)
Chain 4: 
Chain 4:  Elapsed Time: 0.019408 seconds (Warm-up)
Chain 4:                0.016486 seconds (Sampling)
Chain 4:                0.035894 seconds (Total)
Chain 4: 
print(fit)
Inference for Stan model: stan-5a7c20ea0b45.
4 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=4000.

      mean se_mean   sd  2.5%   25%   50%   75% 97.5% n_eff Rhat
y[1] -0.02    0.02 1.02 -2.01 -0.72 -0.01  0.67  2.01  1897    1
y[2]  0.10    0.06 2.79 -5.72 -1.28  0.06  1.47  6.03  2006    1
lp__ -1.50    0.03 1.22 -4.61 -2.06 -1.22 -0.62 -0.10  1329    1

Samples were drawn using NUTS(diag_e) at Wed Oct  2 12:27:32 2019.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at 
convergence, Rhat=1).
# posterior
stan_plot(fit, point_est = "mean", show_density = TRUE, fill_color = "dodgerblue")
ci_level: 0.8 (80% intervals)
outer_level: 0.95 (95% intervals)

# trace
stan_trace(fit) +
  scale_color_manual(values = c("red", "blue", "green", "black"))
Scale for 'colour' is already present. Adding another scale for 'colour',
which will replace the existing scale.

Python in R Markdown

https://rstudio.github.io/reticulate/#python-in-r-markdown

reticulate & R Notebooks

Create object in R code chunk

require(reticulate)
dat <- c(180, 215, 210, 210, 188, 176, 209, 200)

Manipulate r.dat in Python code chunk

198.5

Access py$avg object in R code chunk

py$avg
[1] 198.5
LS0tCnRpdGxlOiAiRXh0ZW5kaW5nIFIgdG8gYWNjb21tb2RhdGUgJ2JpZyBkYXRhJyBhbmQgaW50ZXJmYWNlIHdpdGggb3RoZXIgc29mdHdhcmUgdGVjaG5vbG9naWVzIgpzdWJ0aXRsZTogIk1EU1IgQ2ggMTctLVRvd2FyZHMgQmlnIERhdGEiCm91dHB1dDogCiAgc2xpZHlfcHJlc2VudGF0aW9uOiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdCAgCi0tLQoKCmBgYHtyIEZyb250IE1hdHRlciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQojIGNsZWFuIHVwIFIgZW52aXJvbm1lbnQKcm0obGlzdCA9IGxzKCkpCgojIGdsb2JhbCBvcHRpb25zCmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsPVRSVUUsIGluY2x1ZGU9VFJVRSkKb3B0aW9ucyhkaWdpdHM9NCkKCiMgbG9hZCBhbGwgcGFja2FnZXMgaGVyZQpsaWJyYXJ5KG1kc3IpCmxpYnJhcnkodGlkeXZlcnNlKQoKbGlicmFyeShiaWdsbSkKbGlicmFyeShiaWdycXVlcnkpCmxpYnJhcnkoREJJKQpsaWJyYXJ5KG1hY2xlaXNoKQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KFJjcHApCmxpYnJhcnkocmV0aWN1bGF0ZSkKbGlicmFyeShyc3RhbikKbGlicmFyeShzcGFya2x5cikKCgojIGlucHV0cyBzdW1tYXJ5CgpgYGAKCgojIEFnZW5kYQoKCiMjIyMgQW5ub3VuY2VtZW50cwoKLSBDaCAxNyBwcm9ncmFtbWluZyBub3RlYm9vayBub3QgYXNzaWduZWQsIGJ1dCAqaGlnaGx5KiByZWNvbW1lbmRlZAotIHNldmVyYWwgc3RlcHMgdGFrZSBzb21lIG5vbi10cml2aWFsIGNvbmZpZ3VyYXRpb24gdGhhdCBJIGRvbid0IHdhbnQgdG8gcmVxdWlyZSBwZXIgc2UKCgojIE1EU1IgQ2ggMTc6IFRvd2FyZHMgYmlnIGRhdGEKCi0gMyBWJ3Mgb2YgYmlnIGRhdGEKICAgIC0gVm9sdW1lCiAgICAtIFZhcmlldHkKICAgIC0gVmVsb2NpdHkKLSAiYmlnIGRhdGEgaXMgd2hlbiB5b3VyIHdvcmtmbG93IGJyZWFrcyIgLS1SYW5keSBQcnVpbQotIGhvdyBiaWcgaXMgImJpZyIgaXMgcmVsYXRpdmUKICAgIC0gcGVuY2lsL3BhcGVyIHdvcmtmbG93OiAzMCByb3dzICYgMyBjb2x1bW5zIGlzICJiaWciCiAgICAtIFRJIDgzIHdvcmtmbG93OiBsYXJnZXIgdGhhbiA5OSByb3dzIChvciBjb2x1bW5zKSBpcyAiYmlnIgogICAgLSBNUyBFeGNlbCB3b3JrZmxvdzogbGFyZ2VyIHRoYW4gYW55IG9mIHRoZSBmb2xsb3dpbmcgY29uc3RyYWludHMgaXMgImJpZyIKICAgICAgICAtIDEsMDQ4LDU3NiByb3dzICgkMl4yMCQpCiAgICAgICAgLSAxNiwzODQgY29sdW1ucyAoJDJeMTQkKQogICAgICAgIC0gMjU1IGNoYXJhY3RlcnMgaW4gYSBjb2x1bW4gKCQyXjggLSAxJCkKICAgIC0gTGFwdG9wIHJ1bm5pbmcgUjogbGFyZ2Ugd2l0aCByZXNwZWN0IHRvIGF2YWlsYWJsZSBtZW1vcnkKCgojIEJpZ2dlc3Qgb2YgdGhlIGJpZy4uLiAoZm9yIHBlcnNwZWN0aXZlKQoKLSB0aGUgTGFyZ2UgSGFkcm9uIENvbGxpZGVyIGluIEdlbmV2YSBnZW5lcmF0ZXMgMjUgcGV0YWJ5dGVzIG9mIGRhdGEgcGVyIHllYXIKICAgIC0gb25lIHBldGFieXRlIGlzIGEgbWlsbGlvbiBnaWdhYnl0ZXMKICAgIC0gYWxsIHRoZSB3b3JrZmxvd3MgYXJlIGJyb2tlbgogICAgLSB0aGV5IGFjdHVhbGx5IG9ubHkgc2F2ZSAwLjAwMSUgb2YgdGhlIGRhdGEgZ2VuZXJhdGVkCi0gT3RoZXJzIGp1c3QgZm9yIGZ1biAoW2Jhc2VkIG9uIDIwMTggRm9yYmVzIGFydGljbGVdKGh0dHBzOi8vd3d3LmZvcmJlcy5jb20vc2l0ZXMvYmVybmFyZG1hcnIvMjAxOC8wNS8yMS9ob3ctbXVjaC1kYXRhLWRvLXdlLWNyZWF0ZS1ldmVyeS1kYXktdGhlLW1pbmQtYmxvd2luZy1zdGF0cy1ldmVyeW9uZS1zaG91bGQtcmVhZC8jN2VmNTA4Yzc2MGJhKSkKICAgIC0gR29vZ2xlIHByb2Nlc3NlcyA0MCwwMDAgc2VhcmNoZXMgKnBlciBzZWNvbmQqCiAgICAtIDMwMCBtaWxsaW9uIHBob3RvcyB1cGxvYWRlZCB0byBGYWNlYm9vayBwZXIgZGF5CiAgICAtIFRoZSBXZWF0aGVyIENoYW5uZWwgcmVjZWl2ZXMgMTgsMDU1LDU1NiBmb3JlY2FzdCByZXF1ZXN0cyAqcGVyIG1pbnV0ZSoKCiFbaHR0cHM6Ly93d3cuaWZsc2NpZW5jZS5jb20vdGVjaG5vbG9neS9ob3ctbXVjaC1kYXRhLWRvZXMtdGhlLXdvcmxkLWdlbmVyYXRlLWV2ZXJ5LW1pbnV0ZS9dKDIwMThiaWdEYXRhLnBuZyl7d2lkdGg9OTUlfQoKCgojIFdoYXQgaGFwcGVucyB3aGVuIGRhdGEgc3RhcnQgZ2V0dGluZyBiaWcgKGluIFIpCgotIFRoZSBkYXRhIG1heSBub3QgbG9hZCBpbnRvIG1lbW9yeQotIEFuYWx5emluZyB0aGUgZGF0YSBtYXkgdGFrZSBhIChyZWFsbHkpIGxvbmcgdGltZQotIFZpc3VhbGl6YXRpb25zIGdldCBtZXNzeQoKIyBTaWRlYmFyIGFib3V0IFIgdnMgU0FTIGZvciBsYXJnZSBkYXRhIHNldHMKCi0gU0FTIGFsbG9jYXRlcyBtZW1vcnkgZHluYW1pY2FsbHkgdG8ga2VlcCBkYXRhIG9uIGRpc2sgKGJ5IGRlZmF1bHQpCi0gUiBsb2FkcyBhbGwgZGF0YSBpbnRvIG1lbW9yeSAoYnkgZGVmYXVsdCkKLSBib3R0b20gbGluZTogCiAgICAtIGJ5IGRlZmF1bHQsIFNBUyBoYW5kbGVzIHZlcnkgbGFyZ2UgZGF0YXNldHMgYmV0dGVyLi4uIGl0ICJqdXN0IHdvcmtzIgogICAgLSBidXQgZG9uJ3QgdXNlIHRoZSBSIGRlZmF1bHQhCgojIE1lbW9yeSBsaW1pdHMgaW4gUgoKLSBJZiB5b3UncmUgcnVubmluZyAzMi1iaXQgUiBvbiBhbnkgT1MsIGl0J2xsIGJlIDIgb3IgMyBHQgogICAgLSBub3RlOiAyR0Igb2YgbWVtb3J5IHVzZWQgYnkgUiBpcyBub3QgdGhlIHNhbWUgYXMgMkdCIG9uIGRpc2sKICAgIC0gT3ZlcmhlYWQgZm9yIFIgdG8ga2VlcCB0cmFjayBvZiB5b3VyIGRhdGEKICAgIC0gTWVtb3J5IHVzZWQgZm9yIGFuYWx5c2lzLCBldGMuCiAgICAtIFByb2JhYmx5IG5vdCBtb3JlIHRoYW4gYWJvdXQgNTAwTUIgb24gZGlzaz8KLSBJZiB5b3UncmUgcnVubmluZyA2NC1iaXQgUiBvbiBhIDY0LWJpdCBPUywgdGhlIHVwcGVyIGxpbWl0IHNob3VsZCBiZSBpbmZpbml0ZSAoYnV0IGl0J3Mgbm90KQogICAgLSBQYWNrYWdlIGBiaWdtZW1vcnlgCiAgICAgICAgLSAiTWFuYWdlIG1hc3NpdmUgbWF0cmljZXMgd2l0aCBzaGFyZWQgbWVtb3J5IGFuZCBtZW1vcnktbWFwcGVkIGZpbGVzIgogICAgICAgIC0gdXNlIGluIHBhcmFsbGVsIGVudmlyb25tZW50cyBjYW4gcHJvdmlkZSBzdWJzdGFudGlhbCBzcGVlZCBhbmQgbWVtb3J5IGVmZmljaWVuY2llcwogICAgICAgIC0gdXNlcyBhIHBvaW50ZXIgdG8gYSBDKysgZGF0YSBzdHJ1Y3R1cmUKICAgICAgICAtIChjb24pIG1hdHJpeCBkYXRhIHN0cnVjdHVyZSByZXF1aXJlcyBob21vZ2VuZWl0eSAKICAgIC0gUGFja2FnZSBgZmZgCiAgICAgICAgLSAiTWVtb3J5LUVmZmljaWVudCBTdG9yYWdlIG9mIExhcmdlIERhdGEgb24gRGlzayBhbmQgRmFzdCBBY2Nlc3MgRnVuY3Rpb25zIgogICAgICAgIC0gcHJvdmlkZXMgZGF0YSBzdHJ1Y3R1cmVzIHRoYXQgYXJlIHN0b3JlZCBvbiBkaXNrIGJ1dCBiZWhhdmUgKGFsbW9zdCkgYXMgaWYgdGhleSB3ZXJlIGluIFJBTQogICAgICAgIC0gdXNlcyBhIHBvaW50ZXIgdG8gYSBmbGF0IGJpbmFyeSBmaWxlIHN0b3JlZCBvbiBkaXNrLCBhbmQgaXQgY2FuIGJlIHNoYXJlZCBhY3Jvc3MgZGlmZmVyZW50IHNlc3Npb25zCiAgICAgICAgLSBwZXJtaXRzIGhldGVyb2dlbmVvdXMgZGF0YSBzdHJ1Y3R1cmVzIAoKCiMgIFN0cmF0ZWdpZXMgd2hlbiBkYXRhIGdldCBiaWcKCi0gTWFrZSB0aGUgZGF0YSBzbWFsbGVyIAotIEdldCBhIGJpZ2dlciBjb21wdXRlciAKLSBBY2Nlc3MgdGhlIGRhdGEgZGlmZmVyZW50bHkKLSBTcGxpdCB1cCB0aGUgZGF0YXNldCBmb3IgYW5hbHlzaXMKCgojIElmIHRoaW5ncyBhcmUganVzdCBzbG93Li4uCgotIHRpbWUgeW91ciBjb2RlCiAgICAtIG9uZS1saW5lcjogCiAgICBgYGAKICAgICAgICBzeXN0ZW0udGltZSg8Y2FsbD4pCiAgICBgYGAKICAgIC0gYWx0ZXJuYXRpdmU6IAogICAgYGBgCiAgICAgICAgc3RhcnRfdGltZSA8LSBwcm9jLnRpbWUoKQogICAgICAgIDxjYWxsPgogICAgICAgIHByb2MudGltZSgpIOKAkyBzdGFydF90aW1lCiAgICBgYGAKLSBwcm9maWxlIHlvdXIgY29kZSB0byBmaW5kIG91dCB3aGljaCBzcGVjaWZpYyBvcGVyYXRpb25zIGFyZSBzbG93aW5nIHlvdSBkb3duCi0gc21hbGwgY2hhbmdlcyAoZS5nLiwgdmVjdG9yaXplIHRvIGF2b2lkIGEgbG9vcCkgY2FuIGhhdmUgaHVnZSBiZW5lZml0CgoKIyMjIyBSZWNhbGwgdGhlIHRlbGxlciBzaW11bGF0aW9uOiAKCmBgYHtyfQoKYW55X2FjdGl2ZSA8LSBmdW5jdGlvbihkZikgewogICMgcmV0dXJuIFRSVUUgaWYgc29tZW9uZSBoYXMgbm90IGZpbmlzaGVkCiAgcmV0dXJuKG1heChkZiRlbmR0aW1lKSA9PSBJbmYpCn0KCm5leHRfY3VzdG9tZXIgPC0gZnVuY3Rpb24oZGYpIHsKICAjIHJldHVybnMgdGhlIG5leHQgY3VzdG9tZXIgaW4gbGluZQogIHJlcyA8LSBmaWx0ZXIoZGYsIGVuZHRpbWUgPT0gSW5mKSAlPiUKICAgIGFycmFuZ2UoYXJyaXZhbCkKICByZXR1cm4oaGVhZChyZXMsIDEpKQp9Cgp1cGRhdGVfY3VzdG9tZXIgPC0gZnVuY3Rpb24oZGYsIGN1c3RfbnVtLCBlbmRfdGltZSkgewogICMgc2V0cyB0aGUgZW5kIHRpbWUgb2YgYSBzcGVjaWZpYyBjdXN0b21lcgogIHJldHVybihtdXRhdGUoZGYsIGVuZHRpbWUgPSBpZmVsc2UoY3VzdG51bSA9PSBjdXN0X251bSwgZW5kX3RpbWUsIGVuZHRpbWUpKSkKfQoKCnRlbGxlcl9zaW0gPC0gZnVuY3Rpb24obiA9IDEvMiwgbSA9IDMvMiwgaG91cnMgPSA2KSB7CiAgIyBzaW11bGF0aW9uIG9mIGJhbmsgd2hlcmUgdGhlcmUgaXMganVzdCBvbmUgdGVsbGVyCiAgIyBuOiBleHBlY3RlZCBudW1iZXIgb2YgY3VzdG9tZXJzIHBlciBtaW51dGUKICAjIG06IGV4cGVjdGVkIGxlbmd0aCBvZiB0cmFuc2FjdGlvbiBpcyBtIG1pbnV0ZXMKICAjIGhvdXJzOiBiYW5rIG9wZW4gZm9yIHRoaXMgbWFueSBob3VycwogIAogIGN1c3RvbWVycyA8LSBycG9pcyhob3VycyAqIDYwLCBsYW1iZGEgPSBuKQogIGFycml2YWwgPC0gbnVtZXJpYyhzdW0oY3VzdG9tZXJzKSkKICBwb3NpdGlvbiA8LSAxCiAgZm9yIChpIGluIDE6bGVuZ3RoKGN1c3RvbWVycykpIHsKICAgIG51bWN1c3QgPC0gY3VzdG9tZXJzW2ldCiAgICBpZiAobnVtY3VzdCAhPSAwKSB7CiAgICAgIGFycml2YWxbcG9zaXRpb246KHBvc2l0aW9uICsgbnVtY3VzdCAtIDEpXSA8LSByZXAoaSwgbnVtY3VzdCkKICAgICAgcG9zaXRpb24gPC0gcG9zaXRpb24gKyBudW1jdXN0CiAgICB9CiAgfQogIGR1cmF0aW9uIDwtIHJleHAobGVuZ3RoKGFycml2YWwpLCByYXRlID0gMS9tKSAgIyBFW1hdPW0KICBkZiA8LSBkYXRhLmZyYW1lKGFycml2YWwsIGR1cmF0aW9uLCBjdXN0bnVtID0gMTpsZW5ndGgoZHVyYXRpb24pLCAKICAgICAgICAgICAgICAgICAgIGVuZHRpbWUgPSBJbmYsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKICAKICBlbmR0aW1lIDwtIDAgIyBzZXQgdXAgYmVnaW5uaW5nIG9mIHNpbXVsYXRpb24KICB3aGlsZSAoYW55X2FjdGl2ZShkZikpIHsgIyBhbnlvbmUgbGVmdCB0byBzZXJ2ZQogICAgbmV4dF9vbmUgPC0gbmV4dF9jdXN0b21lcihkZikKICAgIG5vdyA8LSBpZmVsc2UobmV4dF9vbmUkYXJyaXZhbCA+PSBlbmR0aW1lLCBuZXh0X29uZSRhcnJpdmFsLCBlbmR0aW1lKQogICAgZW5kdGltZSA8LSBub3cgKyBuZXh0X29uZSRkdXJhdGlvbgogICAgZGYgPC0gdXBkYXRlX2N1c3RvbWVyKGRmLCBuZXh0X29uZSRjdXN0bnVtLCBlbmR0aW1lKQogIH0KICBkZiA8LSBtdXRhdGUoZGYsIHRvdGFsdGltZSA9IGVuZHRpbWUgLSBhcnJpdmFsKQogIHJldHVybihmYXZzdGF0cyh+IHRvdGFsdGltZSwgZGF0YSA9IGRmKSkKfQoKYGBgCgoKIyBQcm9maWxpbmcgdGhlIHRlbGxlciBzaW11bGF0aW9uIAoKLSBsZXQncyBzZWUgd2hlcmUgdGhlIHRpbWUgZ29lcy4uLgotIG5vdCBiYWQgaW4gdGhpcyBjYXNlLCBidXQgeW91IGNvdWxkIGRpc2NvdmVyIGludGVyZXN0aW5nIHRoaW5ncy4uLiBsaWtlCiAgICAtIG1heWJlIHNvbWUgbW9kZWxpbmcgZnVuY3Rpb24gZGVmYXVsdHMgdG8gYm9vdHN0cmFwIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIHRoYXQgeW91IGRvbuKAmXQgY2FyZSBhYm91dCB3aXRoIDEwMDAgaXRlcmF0aW9ucyBwZXIgbW9kZWwKICAgIC0geW91IGRpZCBzb21ldGhpbmcgZm9yIGV2ZXJ5IGxpbmUgb2YgeW91ciBodWdlIGRhdGEgZnJhbWUgYW5kIHRoZW4gY29tYmluZSByZXN1bHRzIHVzaW5nIGBjKClgIG9yIGByYmluZCgpYCByYXRoZXIgdGhhbiBhc3NpZ25pbmcgdG8gYSBwcmVhbGxvY2F0ZWQgdmVjdG9yIG9yIG1hdHJpeAoKCmBgYHtyfQpScHJvZigiVGVsbGVyU2ltUHJvZmlsZSIpCnRlbGxlcl9zaW0oKQpScHJvZihOVUxMKQpoZWFkKHN1bW1hcnlScHJvZigiVGVsbGVyU2ltUHJvZmlsZSIpJGJ5LnNlbGYsIDIwKQpgYGAKCgojIFNUSUxMIHNsb3c/IFRyeSBgYmlnbG1gCgotIGBiaWdsbWAgcGFja2FnZSBoYXMgYW4gZWZmaWNpZW50IGFsdGVybmF0aXZlIHRvIHRoZSBgbG1gIGZ1bmN0aW9uCi0gaXQgY2FuIGV2ZW4gZml0IGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbHMgKHJlZ3Jlc3Npb24gJiBsb2dpc3RpYyByZWdyZXNzaW9uKSB3aXRoIGRhdGEgZnJhbWVzIHRoYXQgYXJlIGxhcmdlciB0aGFuIG1lbW9yeQoKYGBge3J9CnJlcXVpcmUoYmlnbG0pCgpuIDwtIDIwMDAwCnAgPC0gNTAwCmQgPC0gYXMuZGF0YS5mcmFtZShtYXRyaXgocm5vcm0obiAqIChwICsgMSkpLCBuY29sID0gKHAgKyAxKSkpCmV4cGxfdmFycyA8LSBwYXN0ZShwYXN0ZTAoIlYiLCAyOihwKzEpKSwgY29sbGFwc2UgPSAiICsgIikKbXlfZm9ybXVsYSA8LSBhcy5mb3JtdWxhKHBhc3RlKCJWMSB+ICIsIGV4cGxfdmFycykpCgojIHByb2ZpbGUgYGxtYCB2cyBgYmlnbG1gCnN5c3RlbS50aW1lKGxtKG15X2Zvcm11bGEsIGRhdGEgPSBkKSkKCnN5c3RlbS50aW1lKGJpZ2xtKG15X2Zvcm11bGEsIGRhdGEgPSBkKSkKYGBgCgoKIyBOZXh0IHN0ZXA6IHBhcmFsbGVsIHByb2Nlc3NpbmcKCi0gKipQYXJhbGxlbCBwcm9jZXNzaW5nKiogaXMgYmFzaWNhbGx5IGZhcm1pbmcgb3V0IHN1YnRhc2tzIHRvIGluZGVwZW5kZW50IHByb2Nlc3NvcnMsIHRoZW4gbWVyZ2luZyByZXN1bHRzCi0gZWZmZWN0aXZlbHkganVzdCBhbGxvY2F0ZXMgbW9yZSBSQU0gZm9yIHRoZSBwcm9ibGVtCgpgYGB7cn0KbXlfY29yZXMgPC0gZGV0ZWN0Q29yZXMoKQpteV9jb3JlcwpgYGAKCgotICoqZW1iYXJhc3NpbmdseSBwYXJhbGxlbCBjb21wdXRpbmcqKgogICAgLSBJIG5lZWQgdG8gcmVwZWF0IHRoZSBzYW1lIHRhc2sgbWFueSB0aW1lcwogICAgLSBvcmRlciBvZiBpbXBsZW1lbnRhdGlvbiBkb2Vzbid0IG1hdHRlcgotIEkgaGF2ZSA0IHRvdGFsIGNvcmVzLCBidXQgeW91IHNob3VsZCBhbHdheXMgc2F2ZSBvbmUgZm9yIHlvdXIgb3BlcmF0aW5nIHN5c3RlbQotIGUuZy4sIGNvbXBhcmlzb24gZm9yIHNldmVyYWwgaXRlcmF0aW9ucyBvZiB0ZWxsZXIgc2ltdWxhdGlvbgoKYGBge3J9CmsgPC0gNQoKIyB3aXRob3V0IHBhcmFsbGVsIHByb2Nlc3NpbmcKc3lzdGVtLnRpbWUobGFwcGx5KDE6aywgdGVsbGVyX3NpbSkpCgojIHBhcmFsbGVsaXplIHdpdGggMyBjb3JlcwpzeXN0ZW0udGltZShtY2xhcHBseSgxOmssIHRlbGxlcl9zaW0sIG1jLmNvcmVzID0gbXlfY29yZXMgLSAxKSkKYGBgCgoKIyBNYXBSZWR1Y2UgKHBhcmFsbGVsaXphdGlvbiB0aGF0J3Mgbm90IGVtYmFyYXNzaW5nPykKCi0gcHJvZ3JhbW1pbmcgcGFyYWRpZ20gZm9yIHBhcmFsbGVsIGNvbXB1dGluZwogICAgLSB0d28gcGhhc2UgYWxnb3JpdGhtCiAgICAtICoqbWFwKiotLWZhcm0gb3V0IHBhcmFsbGl6ZWFibGUgdGFzayB0byBtYW55IG1hY2hpbmVzCiAgICAtICoqcmVkdWNlKiotLWNvbWJpbmUgcmVzdWx0cwotIHRyaWNreSBwYXJ0OiAqKnlvdSoqIGhhdmUgdG8gZGVmaW5lIHRoZSBgbWFwYCBmdW5jdGlvbiBhbmQgdGhlIGByZWR1Y2VgIGZ1bmN0aW9uCi0gbmVlZHMgc29mdHdhcmUgaW1wbGVtZW50YXRpb24KICAgIC0gSGFkb29wCiAgICAtIFNwYXJrCgojIEhhZG9vcCAmIFNwYXJrCgotIEhhZG9vcCB3YXMgZmlyc3QgdG8gcmVhbGx5IHRhY2tsZSBNYXBSZWR1Y2UgCi0gSGFkb29wIE1hcFJlZHVjZSBoYXMgYmVlbiBzdXBlcnNlZGVkIGJ5IFNwYXJrLCAKICAgIC0gdG9vbHMgdGhhdCBlbWVyZ2VkIGFzIHRoZSAiZWNvc3lzdGVtIiBhcm91bmQgaXQgYXJlIHN0aWxsIHBvcHVsYXIgKEhERlMpCiAgICAtICJsZWdhY3kiIHByb2plY3RzIG1pZ2h0IHN0aWxsIHVzZSBIYWRvb3AgTWFwUmVkdWNlCi0gQXBhY2hlIFNwYXJrIGlzIGNvbnNpZGVyZWQgc3VwZXJpb3IgZm9yIGEgZmV3IHJlYXNvbnMKICAgIC0gaGFkIHRoZSBiZW5lZml0IG9mIGltcGxlbWVudGluZyBsZXNzb25zIGxlYXJuZWQgZnJvbSBIYWRvb3AKICAgIC0ga2VlcCB0aGUgZ29vZC0tSERGUyAoSGFkb29wIERpc3RyaWJ1dGVkIEZpbGUgU3lzdGVtKSBmb3IgZGlzayBzdG9yYWdlCiAgICAtIGltcHJvdmUgdGhlIHdlYWtuZXNzZXMtLXByaW9yaXRpemUgUkFNIHJhdGhlciB0aGFuIGRpc2sgc3RvcmFnZSB3aGVuZXZlciBwb3NzaWJsZQoKCiMgSW50ZXJmYWNlIHdpdGggU3BhcmsKCi0gU3BhcmsgcHJvdmlkZXMgcHJvdmlkZXMgYW4gaW50ZXJmYWNlIGZvciBwcm9ncmFtbWluZyBlbnRpcmUgY2x1c3RlcnMKLSBhICoqY29tcHV0ZXIgY2x1c3RlcioqIGlzIGEgc2V0IG9mIGNvbm5lY3RlZCBjb21wdXRlcnMgdGhhdCB3b3JrIHRvZ2V0aGVyIGFzIGEgc2luZ2xlIHN5c3RlbQotIHRoZSBgc3BhcmtseXJgIHBhY2thZ2UgaW4gUiBtYWtlcyBpdCBlYXN5IHRvIAogICAgLSBpbnN0YWxsIGEgbG9jYWwgU3BhcmsgY2x1c3RlciAoZnJvbSB3aXRoaW4gUikKICAgIC0gY29ubmVjdCB0byBhIGxvY2FsIG9yIHJlbW90ZSBjbHVzdGVyCgpgYGB7ciBldmFsPUZBTFNFfQpyZXF1aXJlKHNwYXJrbHlyKQojIHNwYXJrX2luc3RhbGwoKSAgICMgb25seSBvbmNlIHBlciBtYWNoaW5lCmBgYAoKCiMgSW50ZXJmYWNlIHdpdGggU3BhcmsKCgpgYGB7cn0KIyBtb2RpZnkgbWFzdGVyIHRvIGNvbm5lY3QgdG8gYSByZW1vdGUgU3BhcmsgY2x1c3RlcgpzYyA8LSBzcGFya19jb25uZWN0KG1hc3RlciA9ICJsb2NhbCIpCmNsYXNzKHNjKQpgYGAKCgpgYGB7cn0KYmFieW5hbWVzX3RibCA8LSAKICBzYyAlPiUKICBjb3B5X3RvKGJhYnluYW1lczo6YmFieW5hbWVzLCAiYmFieW5hbWVzIiwgb3ZlcndyaXRlID0gVFJVRSkKCmNsYXNzKGJhYnluYW1lc190YmwpCmBgYAoKCiMgQ291bnRpbmcgTWF0dGhld3MKCmBgYHtyfQpiYWJ5bmFtZXNfdGJsICU+JQogIGZpbHRlcihuYW1lID09ICJNYXR0aGV3IikgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgc3VtbWFyaXNlKE4gPSBuKCksIAogICAgICAgICAgICB0b3RhbF9iaXJ0aHMgPSBzdW0obikpICU+JQogIGFycmFuZ2UoZGVzYyh0b3RhbF9iaXJ0aHMpKSAlPiUKICBoZWFkKCkKYGBgCgoKIyBGcm9tIGBkcGx5cmAgdG8gU1FMCgotIHdoZW5ldmVyIGBkcGx5cmAgbWVldHMgYW4gb2JqZWN0IHdpdGggY2xhc3MgYHRibF9zcWxgIChsaWtlIGBiYWJ5bmFtZXNfdGJsYCksIGBkcGx5cmAgKiphdXRvbWF0aWNhbGx5KiogdHJhbnNsYXRlcyB0aGUgUiBwaXBlbGluZSBpbnRvICoqU1FMKioKLSAqKlNRTCoqIChzdHJ1Y3R1cmVkIHF1ZXJ5IGxhbmd1YWdlKSBpcyBhIHdpZGVseSB1c2VkIGxhbmd1YWdlIGZvciBxdWVyeWluZyByZWxhdGlvbmFsIGRhdGFiYXNlcywgYW1vbmcgb3RoZXIgcHVycG9zZXMKLSBRdWVyaWVzIGluIFNRTCBzdGFydCB3aXRoIHRoZSBgU0VMRUNUYCBrZXl3b3JkIGFuZCBjb25zaXN0IHNldmVyYWwgY2xhdXNlcyB3aGljaCAqKm11c3QgdG8gYmUgd3JpdHRlbiBpbiBvcmRlcioqOyBiYXNpY2FsbHkKICAgIC0gYFNFTEVDVGAgKCoqcmVxdWlyZWQqKiktLWxpa2UgYHNlbGVjdCgpYCBpbiBgZHBseXJgIChhbmQgcG9zc2libHkgY29tYmluZWQgd2l0aCBgbXV0YXRlKClgKQogICAgLSBgRlJPTWAgKCoqcmVxdWlyZWQqKiktLWxpa2UgdGFibGUgYmVmb3JlIHRoZSBmaXJzdCBgJT4lYCBpbiBgZHBseXJgCiAgICAtIGBKT0lOYC0tbGlrZSBgam9pbigpYCB2ZXJicyBpbiBgZHBseXJgCiAgICAtIGBXSEVSRWAtLWxpa2UgYGZpbHRlcigpYCB2ZXJiIGluIGBkcGx5cmAKICAgIC0gYEdST1VQIEJZYC0tbGlrZSBgZ3JvdXBfYnkoKWAgdmVyYiBpbiBgZHBseXJgCiAgICAtIGBIQVZJTkdgLS1saWtlIHVzaW5nIGEgc2Vjb25kIGBmaWx0ZXIoKWAgaW4gYGRwbHlyYCBhZnRlciB0aGUgcm93cyBoYXZlIGFscmVhZHkgYmVlbiAKICAgIC0gYE9SREVSIEJZYC0tbGlrZSBgYXJyYW5nZSgpYCB2ZXJiIGluIGBkcGx5cmAgCiAgICAtIGBMSU1JVGAtLXNvcnQgb2YgbGlrZSBgaGVhZCgpYCBidXQgbW9yZSB2ZXJzYXRpbGUKLSBGb3IgZXhhbXBsZSwgbGV0J3MgcmV2aXNpdCBvdXIgcHJldmlvdXMgYGRwbHlyYCBxdWVyeQoKYGBge3J9CnEgPC0gCiAgYmFieW5hbWVzX3RibCAlPiUKICBmaWx0ZXIobmFtZSA9PSAiTWF0dGhldyIpICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIHN1bW1hcmlzZShOID0gbigpLCAKICAgICAgICAgICAgdG90YWxfYmlydGhzID0gc3VtKG4pKSAlPiUKICBhcnJhbmdlKGRlc2ModG90YWxfYmlydGhzKSkgJT4lCiAgaGVhZCgpCgpxCmBgYAoKYGBge3J9CnNob3dfcXVlcnkocSkKYGBgCgojIFF1ZXJ5aW5nIHRoZSBTcGFyayBjbHVzdGVyCgotIFNwYXJrIGlzIGEgcGFyYWxsZWxpemVkIHRlY2hub2xvZ3kgZGVzaWduZWQgdG8gc3VwZXJzZWRlIFNRTCwgYnV0IGl0J3Mgc3RpbGwgdXNlZnVsIHRvIGtub3cgU1FMIGluIG9yZGVyIHRvIHVzZSBTcGFyawotIGhlcmUsIHdlJ2xsIHF1ZXJ5IHRoZSBTcGFyayBjbHVzdGVyIHVzaW5nIHRoZSBjb25uZWN0aW9uIHdlJ3ZlIGRlZmluZWQgYHNjYCB3aXRoIHRoZSBTUUwgc3RhdGVtZW50IGVxdWl2YWxlbnQgdG8gb3VyIGBkcGx5cmAgd3JhbmdsaW5nCgpgYGB7cn0KcmVxdWlyZShEQkkpCgpkYkdldFF1ZXJ5KGNvbm4gPSBzYywgc3RhdGVtZW50ID0gIgogICAgICAgICAgICAgICAgICAgICAgICAgU0VMRUNUIHllYXIsIHN1bSgxKSBhcyBOLCBzdW0obikgYXMgdG90YWxfYmlydGhzCiAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIGJhYnluYW1lcwogICAgICAgICAgICAgICAgICAgICAgICAgV0hFUkUgbmFtZSA9PSAnTWF0dGhldycKICAgICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIHllYXIKICAgICAgICAgICAgICAgICAgICAgICAgIE9SREVSIEJZIHRvdGFsX2JpcnRocyBkZXNjCiAgICAgICAgICAgICAgICAgICAgICAgICBMSU1JVCA2CiAgICAgICAgICAgICAgICAgICAgICAgICAiKQpgYGAKCgojIE1vZGVsaW5nIHdpdGggU3BhcmsKCi0gYHdoYXRlbHlfMjAxNWAgaGFzIHNvbWUgd2VhdGhlciBkYXRhIGZyb20gTWFzc2FjaHVzZXR0cyAoaW4gdGhlIGBtYWNsZWlzaGAgcGFja2FnZSkKLSBTcGFyayBoYXMgYSBtYWNoaW5lIGxlYXJuaW5nIGxpYnJhcnkgd2hpY2ggaW5jbHVkZXMgbWFueSBvZiB0aGUgc3VwZXJ2aXNlZC91bnN1cGVydmlzZWQgbGVhcm5pbmcgdG9vbHMgd2UndmUgZGlzY3Vzc2VkIHRoaXMgc2VtZXN0ZXIKLSBsZXQncyB1c2UgU3BhcmsgdG8gZml0IGEgbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbCBhcyBhbiBleGFtcGxlCgpgYGB7cn0KcmVxdWlyZShtYWNsZWlzaCkKCndlYXRoZXJfdGJsIDwtIGNvcHlfdG8oc2MsIHdoYXRlbHlfMjAxNSwgb3ZlcndyaXRlID0gVFJVRSkKCndlYXRoZXJfdGJsICU+JQogIHNwYXJrbHlyOjptbF9saW5lYXJfcmVncmVzc2lvbihyYWluZmFsbCB+IHRlbXBlcmF0dXJlICsgcHJlc3N1cmUgKyByZWxfaHVtaWRpdHkpICU+JQogIHN1bW1hcnkoKQoKYGBgCgoKCgojIEFsdGVybmF0aXZlcyB0byBTUUwgKEdvb2dsZSBCaWdRdWVyeSkKCgpgYGB7cn0KcmVxdWlyZShiaWdycXVlcnkpCgpwcm9qZWN0X2lkIDwtICJzdGF0LTM4MC1jbGFzcy1kZW1vIiAgICMgQmVja21hbidzIGdvb2dsZSBjbG91ZCBwcm9qZWN0IElECgpzcWwgPC0gIlNFTEVDVCB3b3JkLCBjb3VudChkaXN0aW5jdCBjb3JwdXMpIGFzIG51bVBsYXlzLCBzdW0od29yZF9jb3VudCkgYXMgTgogICAgICAgIEZST00gW3B1YmxpY2RhdGE6c2FtcGxlcy5zaGFrZXNwZWFyZV0KICAgICAgICBHUk9VUCBCWSB3b3JkCiAgICAgICAgT1JERVIgQlkgTiBkZXNjCiAgICAgICAgTElNSVQgMTAKICAgICAgICAiCnF1ZXJ5X2V4ZWMocXVlcnkgPSBzcWwsIHByb2plY3QgPSBwcm9qZWN0X2lkKQoKYGBgCgoKIyBgUmNwcGAKCi0gYFJDUFA6OmNwcEZ1bmN0aW9uKClgIGFsbG93cyB5b3UgdG8gd3JpdGUgQysrIGZ1bmN0aW9ucyBpbiBSCi0gYFJjcHA6OnNvdXJjZUNwcCgpYCBsb2FkcyBhIEMrKyBmaWxlIGZyb20gZGlzayBpbiB0aGUgc2FtZSB3YXkgeW91IHVzZSBzb3VyY2UoKSB0byBsb2FkIGEgZmlsZSBvZiBSIGNvZGUuIAotIFJjcHAgd2lsbCBjb21waWxlIHRoZSBDKysgY29kZSBhbmQgY29uc3RydWN0IGFuIFIgZnVuY3Rpb24gdGhhdCBjb25uZWN0cyB0byB0aGUgY29tcGlsZWQgQysrIGZ1bmN0aW9uCi0gbW9yZSBoZXJlOiA8aHR0cDovL2Fkdi1yLmhhZC5jby5uei9SY3BwLmh0bWw+CgoKYGBge3J9CnJlcXVpcmUoUmNwcCkKCiMgd3JpdGUgYSBzaW1wbGUgZnVuY3Rpb24gaW4gQysrCgpjcHBGdW5jdGlvbignaW50IGFkZGVtdXAoaW50IHgsIGludCB5LCBpbnQgeikgewogIGludCBzdW0gPSB4ICsgeSArIHo7CiAgcmV0dXJuIHN1bTsKfScpCgoKIyBSIHJlY29nbml6ZXMgYGFkZGVtdXBgIGxpa2UgYW55IG90aGVyIGZ1bmN0aW9uCmFkZGVtdXAKYGBgCgpgYGB7cn0KYWRkZW11cCgyLCA0LCA2KQpgYGAKCgoKIyBTdGFuCgotIEJheWVzaWFuIHN0YXRpc3RpY2FsIGluZmVyZW5jZSB3aXRoIE1DTUMgc2FtcGxpbmcKLSBTdGFuIG1vZGVsIHdpdGhpbiB0aGUgY29kZSBjaHVuayBpcyBjb21waWxlZCBpbnRvIGEgInN0YW5tb2RlbCIgb2JqZWN0IAotIHJlc3VsdCBhc3NpZ25lZCB0byBhIHZhcmlhYmxlIHdpdGggdGhlIG5hbWUgZ2l2ZW4gYnkgdGhlIGBvdXRwdXQudmFyYCBvcHRpb24KCgpgYGB7c3Rhbiwgb3V0cHV0LnZhcj0iZXgxIn0KcGFyYW1ldGVycyB7CiAgcmVhbCB5WzJdOwp9Cm1vZGVsIHsKICB5WzFdIH4gbm9ybWFsKDAsIDEpOwogIHlbMl0gfiBkb3VibGVfZXhwb25lbnRpYWwoMCwgMik7Cn0KYGBgCgoKYGBge3J9CmxpYnJhcnkocnN0YW4pCmZpdCA8LSBzYW1wbGluZyhleDEsIGNvcmVzID0gMykKcHJpbnQoZml0KQpgYGAKCmBgYHtyfQojIHBvc3RlcmlvcgpzdGFuX3Bsb3QoZml0LCBwb2ludF9lc3QgPSAibWVhbiIsIHNob3dfZGVuc2l0eSA9IFRSVUUsIGZpbGxfY29sb3IgPSAiZG9kZ2VyYmx1ZSIpCgojIHRyYWNlCnN0YW5fdHJhY2UoZml0KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsICJibHVlIiwgImdyZWVuIiwgImJsYWNrIikpCgpgYGAKCgojIFB5dGhvbiBpbiBSIE1hcmtkb3duCgo8aHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9yZXRpY3VsYXRlLyNweXRob24taW4tci1tYXJrZG93bj4KCi0gYHJldGljdWxhdGVgIHBhY2thZ2UgaW5jbHVkZXMgYSBQeXRob24gZW5naW5lIGZvciBSIE1hcmtkb3duOgogICAgLSBSdW4gUHl0aG9uIGNodW5rcyBpbiBhIHNpbmdsZSBQeXRob24gc2Vzc2lvbiBlbWJlZGRlZCB3aXRoaW4geW91ciBSIHNlc3Npb24gKHNoYXJlZCB2YXJpYWJsZXMvc3RhdGUgYmV0d2VlbiBQeXRob24gY2h1bmtzKQogICAgLSBQcmludGluZyBvZiBQeXRob24gb3V0cHV0LCBpbmNsdWRpbmcgZ3JhcGhpY2FsIG91dHB1dCBmcm9tIGBtYXRwbG90bGliYC4KICAgIC0gQWNjZXNzIHRvIG9iamVjdHMgY3JlYXRlZCB3aXRoaW4gUHl0aG9uIGNodW5rcyBmcm9tIFIgdXNpbmcgdGhlIGBweWAgb2JqZWN0IChlLmcuIGBweSR4YCB3b3VsZCBhY2Nlc3MgYW4gYHhgIHZhcmlhYmxlIGNyZWF0ZWQgd2l0aGluIFB5dGhvbiBmcm9tIFIpLgogICAgLSBBY2Nlc3MgdG8gb2JqZWN0cyBjcmVhdGVkIHdpdGhpbiBSIGNodW5rcyBmcm9tIFB5dGhvbiB1c2luZyB0aGUgYHJgIG9iamVjdCAoZS5nLiBgci54YCB3b3VsZCBhY2Nlc3MgdG8gYHhgIHZhcmlhYmxlIGNyZWF0ZWQgd2l0aGluIFIgZnJvbSBQeXRob24pCi0gQnVpbHQgaW4gY29udmVyc2lvbiBmb3IgbWFueSBQeXRob24gb2JqZWN0IHR5cGVzLCBpbmNsdWRpbmcgYE51bVB5YCBhcnJheXMgYW5kIGBQYW5kYXNgIGRhdGEgZnJhbWVzLgoKCiMgYHJldGljdWxhdGVgICYgUiBOb3RlYm9va3MKCi0gYHJldGljdWxhdGVgIHBhY2thZ2UgZG9lcyBtYWtlIGl0IHByZXR0eSBlYXN5IHRvIGdvIGJhY2sgYW5kIGZvcnRoIGJldHdlZW4gUiAmIFB5dGhvbiBvYmplY3RzCi0gaXQgImp1c3Qgd29ya3MiIHdoZW4geW91IGNvbXBpbGUgUk1hcmtkb3duIAotIHRoZSBSIE5vdGVib29rIGlzbid0IHlldCBpcm9uZWQgb3V0IGluIHRoZSBjdXJyZW50IHByb2R1Y3Rpb24gcmVsZWFzZSAodjEuMSkKICAgIC0gcHJldmlldyB3aWxsIHRocm93IGVycm9ycywgZXZlbiB3aGVuIHRoZSBjb2RlIGlzIGNvcnJlY3QKICAgIC0gUiBOb3RlYm9vayBpcyBubyBiZXR0ZXIgdGhhbiBwcmV2aWV3CiAgICAtIFRoaXMgd2lsbCBiZSBmaXhlZCBpbiBSU3R1ZGlvIHYxLjIgWyhpbnN0YWxsIHByZXZpZXcgaGVyZS4uLildKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Byb2R1Y3RzL3JzdHVkaW8vZG93bmxvYWQvcHJldmlldy8pICAKICAgICAgICAtIHlvdSBzaG91bGQgaW5zdGFsbCBpdCAoYWZ0ZXIgdGhlIHNlbWVzdGVyKQogICAgICAgIC0gSSBnZW5lcmFsbHkgZG9uJ3QgcmVjb21tZW5kIG1ham9yIHVwZGF0ZXMgZHVyaW5nIHRoZSBzZW1lc3RlciBpZiB5b3UgY2FuIGF2b2lkIGl0CgoKIyMjIyBDcmVhdGUgb2JqZWN0IGluIFIgY29kZSBjaHVuawoKYGBge3J9CnJlcXVpcmUocmV0aWN1bGF0ZSkKZGF0IDwtIGMoMTgwLCAyMTUsIDIxMCwgMjEwLCAxODgsIDE3NiwgMjA5LCAyMDApCmBgYAoKCiMjIyMgTWFuaXB1bGF0ZSBgci5kYXRgIGluIFB5dGhvbiBjb2RlIGNodW5rCgpgYGB7cHl0aG9ufQojIEltcG9ydCB0aGUgbnVtcHkgcGFja2FnZQppbXBvcnQgbnVtcHkKCiMgQ3JlYXRlIGEgTnVtcHkgYXJyYXkgZnJvbSBkYXRhOiBucF9kYXRhCm5wX2RhdGEgPSBudW1weS5hcnJheShyLmRhdCkKCiMgUHJpbnQgb3V0IG1lYW4gb2YgbnBfZGF0YQphdmcgPSBudW1weS5tZWFuKG5wX2RhdGEpCgpwcmludChhdmcpCmBgYAoKIyMjIyBBY2Nlc3MgYHB5JGF2Z2Agb2JqZWN0IGluIFIgY29kZSBjaHVuawoKYGBge3J9CnB5JGF2ZwpgYGAKCgoKCgoKCg==